﻿using NUnit.Framework;
using Sasa.QualityTools.CodeAnalysis.Core.Models;
using System;
using System.IO;

namespace Sasa.QualityTools.CodeAnalysis.Core.Tests.Unit.Models
{
    [TestFixture]
    public class CodeInspectorTest
    {
        private static readonly string FxCopCmdPath
            = Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES"), "Microsoft FxCop 10.0", "FxCopCmd.exe");

        private static readonly string ResourceDir = Path.Combine(Environment.CurrentDirectory, "Resources", "Models", "CodeInspector");

        private static readonly string OutputPath = Path.Combine(Environment.CurrentDirectory, Constants.CodeAnalysisReportFileName);

        private Core.Models.CodeInspector testee;


        [SetUp]
        public void SetUp()
        {
            testee = new Core.Models.CodeInspector();
        }


        [TestCase(null)]
        [TestCase("")]
        [TestCase(" ")]
        [ExpectedException(typeof(InvalidOperationException))]
        public void PerformShouldThrowExceptionWhenFxCopCmdPathIsInvalid(string value)
        {
            testee.CommandPath = value;
            testee.PerformCodeAnalysis();
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void PerformShouldThrowExceptionWhenFxCopCmdPathDoesNotExist()
        {
            testee.CommandPath = "noexist.exe";
            testee.PerformCodeAnalysis();
        }

        [TestCase(null)]
        [TestCase("")]
        [TestCase(" ")]
        [ExpectedException(typeof(InvalidOperationException))]
        public void PerformShouldThrowExceptionWhenFxCopCmdOptionIsInvalid(string value)
        {
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = null;
            testee.PerformCodeAnalysis();
        }

        [Test]
        public void PerformShouldAnalyzeAssemblyCorrectly()
        {
            var targetPath = Path.Combine(ResourceDir, "TestTarget.dll");
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = String.Format(@"/q /gac /sf /igc /f:""{0}"" /o:""{1}""", targetPath, OutputPath);
            testee.PerformCodeAnalysis();

            Assert.AreEqual(19, testee.Report.Problems.Count);
            Assert.AreEqual(13, testee.Report.Rules.Count);
            Assert.AreEqual(0, testee.Report.Errors.Count);
        }

        [Test]
        public void PerformShouldNotThrowExceptionWhenFxCopReportContainsBreakBuildWarning()
        {
            var targetPath = Path.Combine(ResourceDir, "TestTarget.dll");
            var ruleSet = Path.Combine(ResourceDir, "Custom.ruleset");
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = String.Format(@"/q /gac /sf /igc /rs:=""{0}"" /f:""{1}"" /o:""{2}""", ruleSet, targetPath, OutputPath);
            testee.PerformCodeAnalysis();

            Assert.AreEqual(19, testee.Report.Problems.Count);
            Assert.AreEqual(13, testee.Report.Rules.Count);
            Assert.AreEqual(0, testee.Report.Errors.Count);
        }

        [Test]
        [ExpectedException(typeof(CodeAnalysisException))]
        public void PerformShouldThrowExceptionWhenFxCopCmdReturnsErrorCode()
        {
            var targetPath = Path.Combine(ResourceDir, "noexist.dll");
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = String.Format(@"/q /gac /sf /igc /f:""{0}"" /o:""{1}""", targetPath, OutputPath);
            testee.PerformCodeAnalysis();
        }

        [Test]
        public void PerformShouldAnalyzeAssemblyWhenReferencedAssemblyIsMissing()
        {
            var targetPath = Path.Combine(ResourceDir, "ReferenceError", "TestTarget.ReferenceError.dll");
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = String.Format(@"/c /gac /sf /igc /f:""{0}"" /o:""{1}""", targetPath, OutputPath);
            testee.ReferenceAssemblyPaths.Add(Path.Combine(ResourceDir, "TestTarget.dll"));

            testee.PerformCodeAnalysis();

            Assert.AreEqual(3, testee.Report.Problems.Count);
            Assert.AreEqual(3, testee.Report.Rules.Count);
            Assert.AreEqual(0, testee.Report.Errors.Count);
        }

        [Test]
        public void PerformShouldUseSpecifiedFxCopProject()
        {
            var projectPath = Path.Combine(ResourceDir, "CodeAnalysis.fxcop");
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = String.Format(@"/fo /sf /p:""{0}"" /o:""{1}""", projectPath, OutputPath);

            testee.PerformCodeAnalysis();

            Assert.AreEqual(8, testee.Report.Problems.Count);
            Assert.AreEqual(7, testee.Report.Rules.Count);
            Assert.AreEqual(0, testee.Report.Errors.Count);
        }

        [Test]
        public void PerformShouldWorkCorrectlyWhenNoWarnings()
        {
            var projectPath = Path.Combine(ResourceDir, "CodeAnalysisNoWarning.fxcop");
            testee.CommandPath = FxCopCmdPath;
            testee.CommandOption = String.Format(@"/fo /sf /p:""{0}"" /o:""{1}""", projectPath, OutputPath);

            testee.PerformCodeAnalysis();

            Assert.AreEqual(0, testee.Report.Problems.Count);
            Assert.AreEqual(0, testee.Report.Rules.Count);
            Assert.AreEqual(0, testee.Report.Errors.Count);
        }
    }
}
